OpenPGP 鍵の生成と運用について
考えてみた。人によって答えは変わってくると思う。
事前知識
手元で実験してみる
鍵サーバに公開したり、他人に公開鍵を渡したり、などしなければ恐らく自由。
他人の鍵に署名するときも sign ではなく lsign を使えば信用情報は PC 内に留まる。
ただし実験に使った鍵はちゃんと破棄すること。
思考実験
捨てるつもりで作る Master key の real name や email はデタラメにしておいた方がいいのだろうか。
捨てる前に引っこ抜かれたときにちょっと面倒ではないだろうか。
いや、どうせそんな秘密鍵には信用がつかないだろうし、価値は生まれないだろう。
real name も email も任意の値を設定できるわけだから、そんな使い捨て前提の鍵を拾って利用したところで「お前が適当に他人の名前で作っただけじゃねーの?」という話になるわけで、そういう騙りは通用しないと思うのだな。
Master key を消した場合に Subkey ではできないこと
まず、subkey で subkey の管理はできない
新規で作ったり expire の変更したり
他 key への sign ができない
やり方は後述
Master key の情報変更または移行
// TODO
Real Name を変えたり Email を変えたり、あるいは暗号化方式をより強力にしたり。
そうした edit はできないと思われるが、その代わり鍵の移行 (transition) はできなくはない。この移行というやつも移行機能によって実現しているというわけではなく、新規で key 発行して新旧 key で相互に sign し合い、その上で「俺は key を変えたぜー」とのたまう、というような感じと思われる。
See: https://infra.apache.org/key-transition.html
Master key の生成
最も安全に Master key (主鍵) を生成する方法は、スタンドアロンの Linux 環境、それも過去一度もネットワークに接続させていないものの上で生成することである。
そこまでするかは人による。
スタンドアロン Linux 環境ということなら n/w 遮断した USB ブート Linux とか、Raspberry Pi Zero (Zero の無印の方なら Wi-Fi モジュールが on board にくっついてない。Zero W や WH だと Wi-Fi あり) あたり。
普通の Raspberry Pi でも SD Card に Raspbian イメージ (変なの掴まされてない前提) を焼いた直後のやつで、Wi-Fi に接続しない状態だったら大丈夫かも知れないが。
考慮点としては Raspberry Pi の場合はファイルシステムの暗号化のハードルがありそうな気がしているのだがろうだろうか。経路としては自宅に侵入された上に当該の Raspberry Pi (あるいはその SD Card をぶっこ抜いて) 持ち出すという形になるので…まあ気にするかどうかは自分次第だが。
また、この場合に paperkey を使おうとなると、paperkey のパッケージファイルのインストールという微々たる問題もあるが、それ以上に USB (Bluetooth でもいいかも) 接続したプリンタで印刷したくなってくるわけで、プリンタの選定とドライバ周りが面倒になりそうな予感がする。
普段使いの PC で Master key を生成、保管して始末するまでの間だけ Wi-Fi を切るなり LAN ケーブルを引っこ抜くなりしておけば十分かもしれない。
まあ俺がもしクラッカーの立場だったら… GPG 鍵奪取するようなプログラムを仕込む場合、~/.gnupg を見張っておいて逐次中身をディスク上のどこかに退避させ、ネットワークが復旧した時点でそれを送信するようなコードにするかもなとか思ったりはするが。
暗号化方式の指定
--expert オプションをつけること。
code:shell
# 安全のためにまず (9) ECC and ECC, 続いて (1) Curve 25519 を選択すること!
# あとはインタラクションに従う
# expire は必要に応じて設定すればよい
gpg --expert --full-gen-key
これで ~/.gnupg 配下にブツが設置され、$ gpg -k で鍵の一覧の確認も可能となる。
なお暗号化アルゴリズムの指定方法については https://debconf17.debconf.org/talks/162/ を参考にした。
パスフレーズは必要か
とりあえず設定してる。が、SSH 鍵のパスフレーズが時間稼ぎにしかならないという話もあるし、git commit で GPG 署名する場合は都度パスフレーズの入力を求められて辛いので、正直なくせるものならなくしたい。
なくせない場合、これはもう Git の話になるが、署名なし commit を基本にして push 前に rebase -i する方式にはできないだろうか。
作った master key は普段使い PC から消すべきか
安全ではあるのだが、前述の通り他 key へのサインができなくなる。
で、どうするかというと、地味に面倒だがやりたい場合には以下の手順が提唱されている。 https://www.void.gr/kargig/blog/2013/12/02/creating-a-new-gpg-key-with-subkeys/
USB メモリに master key を突っ込む (人によっては backup media として最初から使っているであろう)
sign したいマシンで USB メモリをマウントする
$ gpg --home=/media/encrypted-usb/.gnupg/ --sign-key 0xSomeones_keyid
というわけで安全性と手間のトレードオフである。
subkey の管理だけの話だったらそんなに気にはしないが。とはいえ sign/lsign もそう頻繁にはやらない(のか?)のであればこれで良いのだろう。
Subkey の生成
スタンドアロンな環境で master key を生成した場合、その master key を使って subkey (副鍵) を作っておくことを忘れない。
そして master key は後述する何かしらの方法で保管し、subkey はそれとは別に USB メモリ等を使って普段使いの PC などに移動させることになる。
どんな subkey を作るか
用途で分けるとか、いろいろありそうではある。
ここは各人のポリシーにより意見が分かれそうだ。
鍵サーバへの登録
もし paperkey により秘密鍵を復元する場合は公開鍵が必要になる。なので公開鍵のバックアップ手段として鍵サーバを使うという発想もなくはない。
純粋にバックアップだけを目的とするならば、データ保全の観点で安全そうなクラウドストレージを視野には入れて良いとは思うが。
Master key の保管手段
オフラインで保管しましょう、というのが一般論。
お手軽度が高いのは USB メモリである。前述の通り master key を消す場合は他 key の署名にも使いやすい。
ただし寿命があるということは忘れないこと。そして仮に USB メモリを使うにしても SLC が理想的ではある。
https://www.pro.logitec.co.jp/houjin/usernavigation/hddssd/1228_03/
この辺の、オフラインで情報を保管するメディアの特性についてはネット上に出てくるであろう。
ちなみに SLC と素朴に検索してもあまり出て来ない上に、高い。
https://www.amazon.co.jp/dp/B006CDWJEO
後述の長寿命メディアとの併用をするなら MLC で十分なんじゃないか?
中には、紙最強!という声もあり paperkey というツールが提供されている。http://www.jabberwocky.com/software/paperkey/
まあ古代の壁画とかいまだに残ってるので原始的な方法こそ強いって言う話は聞いたことがある。あ、もしかして QR コードにして石に掘るとか…まあいいか。
ただし paperkey を使う場合は、何かしらの手段でスタンドアロンの Linux 環境に paperkey をインストールして使うことになるため、そこはひと手間かける必要が出てくることだろう。
Smart card (Yubikey) っていう話もある https://wiki.gnupg.org/SmartCard
master 秘密鍵が必要になった時点で Yubikey をガシャっとはめると使える的な感じ。
USB メモリ + 紙を含めた長期記憶メディア、というのがオフラインでの保管としてはよいところだろう。
ただし日本は地震が多いし、最近は台風も来るし、火災が(周囲の建物からの燃え移りも含め)あるかも知れないし、という災害による物理的被害を考え始めると、一個人が安全にオフラインに情報を残し続けるためには、貸し金庫に紙で保存などになってくるだろうか。
https://manetatsu.com/2018/06/130610/
どーーーーーーーーーーしてもオフラインでの永続的な保管に問題を感じてしまう場合は、Amazon S3 や Google Cloud Storage などのクラウドストレージへの保管を視野に入れることになる。
それらの安全性をどう高めるか?という観点で踏み込み始めるとこれはもう AWS やら GCP やらの記事になってしまうので、設定ノウハウなどについてはこれ以上ここでは言及しない。
もちろん Master key のクラウド上への保管については懐疑的な声が上がることが想定される。やるならば世の中の IaaS のセキュリティ tips 等をよく読み込んだ上での設定を施し、かつ key を共通鍵系で暗号化した上で保管するとか、なんかそんな感じになるだろう。
以上、どの手段を選ぶかは自分の信条と、置かれている状況により決定される。
参考
http://joemphilips.com/post/gpg_memo/
https://postd.cc/secure-yourself-part-1-air-gapped-computer-gpg-and-smartcards/
https://gist.github.com/hatsusato/1d5f0267bc9d02bb24c60bd7acc5a59a
https://www.void.gr/kargig/blog/2013/12/02/creating-a-new-gpg-key-with-subkeys/
で、基本方針は
1. スタンドアローンなラズパイで master key, subkey, revocation_certificate を作る
2. 鍵利用マシーンに pubkey および subkey の private key を imoprt する
3. 普段遣いで署名と暗号化をするのはこの subkey の方
4. 鍵のバックアップには SLC USB メモリを採用し、さらに万全を期すならば Paper key + Brother プリンタ
5. 他人の key への署名には master private key が必要になるので、この時は USB メモリをマウントして master private key をパス指定して使う(つまりローカルに import するようなマネはしない)
実際にやってみた
鍵管理マシン
鍵の発行をするための管理マシンをまずは作る。我が家に余っていた Raspberry Pi 3B+ を使うことにした。
余計なものは不要なので Raspberry Pi OS (32-bit) Lite (Minimal image based on Debian Buster) を用いた。
Raspberry Pi OS のインストール手順はそこら中に落ちていると思うので割愛する。なお、永久にネットワーク接続せずに使うとはいえ一応 pi ユーザのパスワードは変更しておくこと。また、インストール後に決してネットワークの設定をしないこと。
また、sudo raspi-config の Locallization 設定にて、言語とキーボードレイアウトそしてタイムゾーンは必要に応じて変更しておく。デフォルトは en_GB.UTF-8 になっていたので en_US.UTF-8 に変えた。また、タイムゾーンもヨーロッパ系になっていたので住んでいる地域にした。UTC でもいいのだが、時計は都度合わせる必要がある(後述)わけで、そのときにタイムゾーンが一致していると作業が多少楽なため。また念の為 systemctl の stop/disable で networking と wpa_supplicasnt および Bluetooth 用デーモンの hciuart を停止しておいた。起動していても WiFi につなげなければ何も起きないはずだが、使わないので。
さてここまでやったら時計合わせだ。完全にスタンドアローンで使うし、どうせ不要なときは電源を落としておくので時計がどんどん狂っていくのである。date コマンドを打つだけの話ではあるが、今後この鍵管理マシンを起動したら毎度この作業が発生するということを忘れないように。古い日付で鍵が生成され続けるぞ。
date -s "20YY-MM-dd hh:mm:ss"
続いて USB メモリ経由で Paperkey の deb ファイルを渡して dpkg -i にてインストールする。deb ファイルは Debian のパッケージリポジトリから拾ってくる。
https://www.debian.org/distrib/packages
CPU アーキテクチャとしては 64bit 版の arm64 ではなく armhf を選んだ。dpkg -l でプリインストールされているパッケージの確認をしたところ軒並み armhf だったため。
鍵の保管用 USB メモリには Transcend JetFlash 170 512MB を用いた。2020年6月現在、Amazon で 2,826円。
今どきたった 512MB でそんなに高いの?!と思うかもしれないがこいつは SLC なので一般的な USB メモリと比較するとデータの保全性能は抜群だ。
鍵のオフライン保管だけのために使うので容量的にも十分だし、USB 2.0 のみ対応だがそれもまあ問題ないだろう。
さて、ここまで来たらようやく master key の生成となる。
code:shell
# 安全な暗号化アルゴリズムを指定できるようにするため --expert オプションをつけること
gpg --expert --full-gen-key
安全のためにまず (9) ECC and ECC, 続いて (1) Curve 25519 を選択すること。さて expire だがひとまず 1 年に設定しておくこととする。以下が長すぎない expire を設定することの意義を説明した記事である。期限が来た場合には新規で master key を生成する必要はなく、expire の extend をすれば済むようだ。
https://blog.josefsson.org/2014/08/26/the-case-for-short-openpgp-key-validity-periods/
続いて失効証明書、つまり使用しなくなった鍵を revoke するための証明書を作成する。これは鍵サーバに公開した public key に対しても効果を発揮できるようだ。
code:shell
# <key id> は master key にセットしたメールアドレス
# option の順番を逆にするとエラーが出る
gpg --output revocation_certificate.asc --gen-revoke <key id>
失効の理由を次の選択肢から選べと言ってくるが、現時点では何もないので 0 = No reason specified としておいた。さらに description も設定できると言ってくるが空欄で。
今度は subkey を作ろう。
実はここで、どのような subkey を作るかというのも 1 つのテーマになってくる。今回はひとまず汎用的に使える subkey を用意するだけにとどめておく。
作成する subkey は 2 つで、1つは署名と認証用、もう 1 つは暗号化用だ。この 2 つは同じ key にできない。署名用については、認証(SSH 鍵として使える)にも使えるぞフラグがあり、そのフラグを立てるかどうかはユーザが選ぶことができる。認証のみで署名には使わない鍵というのも作れる。
code:shell
gpg --expert --edit-key <key id>
# まずは署名・認証用から
gpg> addkey
# ここで Key の種類を聞かれるので
# (11) ECC (set your own capabilities)
# 続いて立てるフラグを聞いてくるので
# (A) で Authenticate を有効にし (Q) で抜ける
# 続いてにどんな楕円曲線を使うか聞いてくるので昨今の SSH に合わせて
# (1) Curve 25519 を選択する
# 最後に expire を訪ねてくるので master key に合わせて 1y としておく
# 今度は暗号化用の key を作る
gpg> addkey
# (10) ECC (sign only)
# (1) Curve 25519
# 1y
ここからは各種 key の保管作業に入る。
Master key の export をまずは行おう。この中には maseter key も subkey もすべて含まれているようだが、ネットワークにさらされるのは public key の方だけだからきっと大丈夫。ここを厳密に master key だけにしておきたい場合は subkey の生成前に export しておく必要があるということだろう。
code:shell
# たぶん master + 全 sub の master key
gpg --export --armor <key id> > pubkey.gpg
# たぶん master + 全 sub の private key
gpg --export-secret-keys --armor <user-id> > privkey.asc
続いて subkey の private key を export する。複数ある subkey を個別に export したい場合は https://wiki.archlinux.org/index.php/GnuPG#Exporting_subkey を見て行うこと。全 subkey の private key を抜いて来る場合は以下となる。
code:shell
gpg --export-secret-subkeys --armor <user id> subkey.gpg
ここまでで export したもの全て、すなわち以下を USB メモリへ保存する。
revocation_certificate.asc
pubkey.gpg
privkey.asc
subkey.gpg
そして key 保管の仕上げとして、もし念には念を入れたいのであれば Paper key による master private key の出力を行う。
code:shell
gpg --export-secret-key <user id> | paperkey --output paperkey.asc
paperkey.asc の中身を紙に残せばいいわけだが、スタンドアローンな Raspberry Pi からプリンタを使うとなると後述の Brother 機か、気合いの手書きのどちらかになるだろう。が、これを間違えずに手書きって相当しんどいぞ???
ここまで終わったら Raspberry Pi 上の key はすべて抹消しても良い。SD カードを暗号化していなければデータを抜かれて終わりだからだ。
とはいえ…自宅でありながらそこのリスクをヘッジするのかは、各人の判断に委ねたい。
鍵の利用マシン
まずは先程 USB メモリに保存した key を利用マシンに import することになるのだが、うっかり privkey.asc まで import するようなマネは絶対に避ける。何のためにせっかくスタンドアローンなラズパイで鍵発行したのか、となってしまう。
gpg: A45A123C: There is no assurance this key belongs to the named user gpg: stdin: encryption failed: Unusable public key
まずは USB メモリを /path/to/mnt にマウントしたとする。その上で以下を行う。
code:shell
gpg --import /path/to/mnt/pubkey.gpg
gpg --import /path/to/mnt/subkey.gpg
さらに gpg -K で import したものが表示されるかを確認しておくのもよい。ここで表示される結果において、master key に sec# と # つきのマークがあれば master key の private key が含まれていない証拠である模様。
鍵が信頼できないと言われたら
こんな感じのエラーメッセージが出る場合。
code:shell
gpg: ********: There is no assurance this key belongs to the named user gpg: stdin: encryption failed: Unusable public key
これは USB 経由で鍵を移した先のマシンで鍵を信頼する必要がある。
code:shell
$ gpg --edit-key <KEY_ID>
gpg> trust
1 = I don't know or won't say
2 = I do NOT trust
3 = I trust marginally
4 = I trust fully
5 = I trust ultimately
m = back to the main menu
Your decision? 5
Do you really want to set this key to ultimate trust? (y/N) y
Git/GitHub での GPG 利用
以下で public key を出力して結果を GitHub に Setting 画面から登録する。
code:shell
# <user id> の代わりに subkey fingerprint を指定したらその subkey だけ抜けるか?と思ったが無理っぽい
gpg --export --armor <user id>
続いて commit に GPG による署名をするように設定する。
まずは署名用 subkey の fingerprint を確認する。
code:shell
gpg --list-keys --with-subkey-fingerprint <user id>
[SA] マークが付与されているのが今回作った署名用 key だ。そのマークの 1 つ下に表示されている 16 進数がこの subkey の fingerprint である。
続いて commit 署名にこの subkey を使うよう Git に設定するのだが、グローバルに設定したくない場合は以下のコマンドから --global オプションを外すこと。
code:shell
git config --global user.signingkey <fingerprint>
以上により、git commit コマンドに -S オプションを付与することで署名が可能となった。
コミットに署名がついているかの確認は git log --show-signature により可能である。
また、rebase においても -S (--gpg-sign) オプションが存在している。
https://stackoverflow.com/questions/18874281/how-to-retain-commit-gpg-signature-after-interactive-rebase-squashing
さてこの git commit -S はパスフレーズの入力を求められる。幸いなことに勝手に起動する gpg-agent がパスフレーズを一定期間キャッシュしていてくれるおかげで何度も打ち直さずにはすむようだ。
このキャッシュ期間を変更する場合は ~/.gnupg/gpg-agent.conf を編集することになるわけだが、ここは Git のセクションなので gpg-agent については別途。
git commit を常時 -S 状態にするにはさらに以下のコンフィグを行う。
code:shell
git config --global commit.gpgsign true
注意事項
この Raspberry Pi はネットワークにつながっていないので、特に電源を落としたりなんかした場合だと思うが時計が絶望的にずれていく。作業時にはまず時計を手動で合わせるようにすること。
運用編
鍵の期限を伸ばす
鍵の管理マシンにて以下を行う。
code:shell
gpg --edit-key <id>
# これで Master key を選択する
gpg> key 0
# これで指示に従って延長したい期間を入れる
gpg> expire
# 必要な subkey をこれですべて選択する
gpg> key 1
gpg> key 2
# subkey も延長
gpg> expire
# 保存して終了
gpg> save
あとは export 作業。
code:shell
cd /path/to/export-dir
gpg --export --armor <id> > pubkey.gpg
gpg --export-secret-keys --armor <id> > privkey.asc
gpg --export-secret-subkeys --armor <id> subkey.gpg
gpg --output revocation_certificate.asc --gen-revoke <id>
こうして export したものは鍵管理マシンから持ち出すために、USB ドライブに保存する。
続いて USB ドライブを鍵を使いたいマシンに接続・マウントした上で import を行う。
code:shell
gpg --import /path/to/mnt/pubkey.gpg
gpg --import /path/to/mnt/subkey.gpg
これで既存の期限切れの鍵を上書きする形になるようだ。
以上で作業終了。
YubiKey に Private Key を退避させている場合にも、改めてこの作業をする必要はない。privatekey.asc を import していないからだと思う。
YubiKey に GPG 鍵を入れる
心配なら gpg -K ${id} して (オプションの K は大文字) key に sec# や ssb> という表記になっていることを確認する。